/*************************************************************************
|                                                                        |
|   MAIN.C                                                      24.08.94 |
|   ADMAKE Utility:  main program                                        |
|                                                                        |
*************************************************************************/

void print_header (void);

#define VV "4.2"
#define DD "15 May 96"

#ifdef VMS
#define CC "(VAX C)"
#include stdio
#include string
#include errno
#include "h.h"
#define OMITBRK 1
#endif

#ifdef __BORLANDC__
#define CC "(Borland C++)"
#include <stdio.h>
#include <dos.h>
#include <io.h>
#include <dir.h>
#include "h.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
unsigned _stklen = 8192;
#if (__BORLANDC__ < 0x0400)
#define _getdrive() (getdisk()+1)
#define _chdrive(x) setdisk(x - 1)
#else
#include <direct.h>
#endif
#endif

#ifdef __WATCOMC__
#ifdef __OS2__
#define CC "(Watcom C OS/2)"
#else
#define CC "(Watcom C DOS)"
#endif
#include <stdio.h>
#include <dos.h>
#include <io.h>
#include <direct.h>
#include <h.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#define OMITBRK 1
static unsigned maxdrives;
#endif

#ifdef _INTELC32_
#define CC "(Intel C)"
#include <stdio.h>
#include <dos.h>
#include <io.h>
#include <direct.h>
#include "h.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#endif

#ifdef NIX
#ifdef COHERENT
#define CC "(Coherent)"
#else
#define CC "(UNIX)"
#endif
#include <stdio.h>
#include "h.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#ifdef VARARGS
#include <varargs.h>
#else
#include <stdarg.h>
#endif
#define OMITBRK 1
#endif

char *          myname;             /* Name of this program */
static char *   makefile;           /* The make file  */

bool            all = FALSE;        /* Do all targets */
bool            confirm = FALSE;    /* Re-stat target after make */
bool            domake = TRUE;      /* Go through the motions option  */
bool            ignore = FALSE;     /* Ignore exit status option  */
bool            silent = FALSE;     /* Silent option  */
bool            print = FALSE;      /* Print debuging information  */
bool            rules = TRUE;       /* Use inbuilt rules  */
bool            dotouch = FALSE;    /* Touch files instead of making  */
bool            quest = FALSE;      /* Question up-to-dateness of file  */
bool            display = FALSE;    /* Display times */
bool            swap = FALSE;       /* sWap ADMAKE to EMS or disk */
bool            why = FALSE;        /* Explain why we make this target */
bool            tabreqd = FALSE;    /* True if tab required for cmd */
bool            ignoreshell = FALSE;/* True if ignore returns from shell */
uchar           macrotype = '\0';   /* Environment, Command-line, etc */
unsigned int    errorlevel = 0;     /* return from last spawn */

FILE *          ifile[4] = {0};             /* input files */
int             fln[4] = {0,0,0,0};         /* input file line numbers */
char            fname[4][80] = {"stdin"};   /* input file names */
int             nestlvl = 0;                /* nesting level */
char            ifmessage = '#';            /* 'if' statement lead-in */
#ifdef _MAX_DIR
static char     curdir[_MAX_DIR];
static int      curdrive;
#endif

#ifdef PAMOS2
#define OMITBRK 1
#endif

#ifndef OMITBRK
static union REGS regs;         /* registers for BIOS calls */
static unsigned int svbreak;    /* save break state */

/*************************************************************************
|                                                                        |
|   >>>>  cleanbrk                                                       |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
static void CDECL cleanbrk(void)
#else
static void CDECL cleanbreak()
#endif

{
    regs.x.ax = 0x3301;         /* set break state */
    regs.x.dx = svbreak;        /* save state */
    intdos(&regs,&regs);        /* do the dos call */
}

#endif

#ifdef HEAP
extern void pascal _nheapkill( void ) { ; }
#endif

/*************************************************************************
|                                                                        |
|   >>>>  resetcd                                                        |
|                                                                        |
|   reset current [drive and] directory                                  |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
VOID resetcd(void)
#else
VOID resetcd()
#endif

{
#ifdef PAMDOS
#ifdef __ZTC__
    dos_setdrive(curdrive,&maxdrives);
    chdir(curdir);
#else        
#ifdef __WATCOMC__
    _dos_setdrive(curdrive,&maxdrives);
    chdir(curdir);
#else
    _chdrive(curdrive);
    chdir(curdir);
#endif
#endif
#endif
}

/*************************************************************************
|                                                                        |
|   >>>>  usage                                                          |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
void usage(int full)
#else
void usage(full)
int full;
#endif

{
    register int i;

#ifdef SWAPEXEC
    fprintf(stderr, MSG[0], myname);
#else
    fprintf(stderr, MSG[1], myname);
#endif
    if (full)
    {
        for (i = 2; i <= 19; i++) 
        {
#ifndef SWAPEXEC
            if (i == 18) continue;
#endif
            fprintf(stderr,MSG[i]);
        }
    }
    else
        fprintf(stderr,MSG[20],myname);
    exit(PXERROR);
}

/*************************************************************************
|                                                                        |
|   >>>>  fatal                                                          |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
void CDECL fatal(char * msg, ...)
#else
#ifdef VARARGS
void CDECL fatal(msg, va_alist)
char * msg;
va_dcl
#else
void CDECL fatal(msg, ...)
char * msg;
#endif
#endif

{
    va_list argptr;
#ifdef VARARGS
    va_start(argptr);
#else
    va_start(argptr, msg);
#endif
    fprintf(stderr, "%s: ", myname);
#ifdef VFPRINTF_MISSING
    {
        char * p1, *p2, *p3;
        p1 = va_arg(argptr,char *);
        p2 = va_arg(argptr,char *);
        p3 = va_arg(argptr,char *);
        fprintf(stderr,msg,p1,p2,p3);
    }
#else
    vfprintf(stderr, msg, argptr);
#endif

    fputc('\n', stderr);
    va_end(argptr);
    resetcd();
    exit(PXERROR);
}

/*************************************************************************
|                                                                        |
|   >>>>  error                                                          |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
void CDECL error(char * msg, ...)
#else
#ifdef VARARGS
void CDECL error(msg, va_alist)
char * msg;
va_dcl
#else
void CDECL error(msg, ...)
char * msg;
#endif
#endif

{
    va_list argptr;

#ifdef VARARGS
    va_start(argptr);
#else
    va_start(argptr, msg);
#endif
    fprintf(stderr, "%s: ", myname);

#ifdef VFPRINTF_MISSING
    {
        char * p1, *p2, *p3;
        p1 = va_arg(argptr,char *);
        p2 = va_arg(argptr,char *);
        p3 = va_arg(argptr,char *);
        fprintf(stderr,msg,p1,p2,p3);
    }
#else
    vfprintf(stderr, msg, argptr);
#endif

    if (lineno && (nestlvl >= 0)) fprintf(stderr, MSG[21], fname[nestlvl], lineno);
    fputc('\n', stderr);
    va_end(argptr);
    resetcd();
    exit(PXERROR);
}

#ifdef STRDUP_NEEDED

/*************************************************************************
|                                                                        |
|   >>>>  strdup                                                         |
|                                                                        |
|   No strdup in some VMS C, or in Coherent                              |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
char * strdup(char * source)
#else
char * strdup(source)
char * source;
#endif

{
     char * p;

     if ((p = malloc(1 + strlen(source))) == (char *)0) return (char *)0;
     strcpy(p,source);
     return p;
}  
#endif

/*************************************************************************
|                                                                        |
|   >>>>  pstrstr                                                        |
|                                                                        |
|   local strstr, some compiler libraries dont have it                   |
|                                                                        |
*************************************************************************/

#ifdef PANEWFD
char * pstrstr(char * source, char * target)
#else
char * pstrstr(source, target)
char * source;
char * target;
#endif

{
    register char *         s1;
    register char *         t;

    for ( ; *source ; ++source)         /* look along source */
    {
        for (s1 = source, t = target; *s1 == *t; ++s1)
        {
            if (*++t == '\0') return source;    /* match if hit end of targ */
        }
    }
    return (char *)0;
}

/*************************************************************************
|                                                                        |
|   >>>>  main                                                           |
|                                                                        |
*************************************************************************/

int CDECL main (int argc, char ** argv, char ** envp)
{
    register char *         p;          /*  For argument processing  */
    int                     estat = 0;  /*  For question  */
    register struct name *  np;
    int                     i;
    char *                  makeflags;
    int                     egiven = 0;
    bool                    doall = 0;

    myname = "admake";

#ifdef HEAP
    _heapmode = _HEAPTIME;
#endif

#ifdef __WATCOMC__
    envp = environ;
#endif

    macrotype = M_ENV;          /* doing enviroment macros */ 

print_header ();

    while(*envp) 
    {
        p = strchr(*envp,'=');  /* find = in environment string */
        if (p == NULL)          /* no '=' in environment string */
        {                       /* never happens with COMMAND.COM */
            envp++;             /* but needed for MKS Korn Shell */
            continue;
        }   
        i = (int)(p - *envp);
        strncpy(str1,*envp++,i);
        str1[i] = '\0';
        setmacro(str1,p+1);
    }

#ifdef PAMOS2
    setmacro("ADMAKE_OS","OS2");
#endif

#ifdef PAMDOS
    setmacro("ADMAKE_OS","DOS");
#ifdef __ZTC__
    dos_getdrive((unsigned *)&curdrive);
#else
#ifdef __WATCOMC__
    _dos_getdrive((unsigned *)&curdrive);
#else
    curdrive = _getdrive();
#endif
#endif
    getcwd(curdir,_MAX_DIR);
#endif

#ifdef VMS
    setmacro("ADMAKE_OS","VMS");
#endif    

#ifdef UNIX
    setmacro("ADMAKE_OS", "UNIX");
#endif
    
    /* ignore shell return codes when ignore specified */

#ifdef PAMOS2
    ignoreshell = 1;                            /* ignore if OS/2 */
#endif

#ifdef PAMDOS
    ignoreshell |= (0 != getenv("CMDLINE"));    /* ignore if 4dos */
#endif

    macrotype = '\0';

    makeflags = getenv("MAKEFLAGS");
    if (makeflags) argv[0] = makeflags;
    else argv[0] = "";

    while (argc > 0) 
    {
        if (**argv == '\0')     /* empty makeflags */
        {
            argc--;             /*  One less to process  */
            argv++;  
        } 
        else if (**argv == '-') 
        {
            argc--;             /*  One less to process  */
            p = *argv++;        /*  Now processing this one  */

            while (*++p != '\0')
            {
                switch(*p)
                {
                case 'a':       /*  do all targets */
                    doall = '\1';
                    break;
                case 'c':       /*  confirm timestamp after make */
                    confirm++;
                    break;
                case 'd':       /*  debug with timestamps */
                    display++;
                    break;
                case 'e':       /*  Enviroment macros prevail */
                    egiven++;
                    break;
                case 'f':       /*  Alternate file name  */
                    if (*(argv-1) == makeflags) break;
                    if (argc-- <= 0) usage(0);
                    makefile = *argv++;
                    break;
                case 'n':       /*  Pretend mode  */
                    domake = FALSE;
                    break;
                case 'i':       /*  Ignore fault mode  */
                    ignore = TRUE;
                    break;
                case 's':       /*  Silent about commands  */
                    silent = TRUE;
                    break;
                case 'p':
                    if (*(argv-1) != makeflags) print = TRUE;
                    break;
                case 'r':
                    rules = FALSE;
                    break;
                case 't':
                    dotouch = TRUE;
                    break;
                case 'q':
                    quest = TRUE;
                    break;
                case 'v':
                    fprintf(stderr, MSG[22], VV, CC, DD);
                    /* drop through */
                case 'h':
                    usage(1);
                    break;
                case 'u':
                    tabreqd = TRUE;
                    break;
#ifdef SWAPEXEC
                case 'w':
                    swap = TRUE;
                    break;
#endif
                case 'y':
                    why = TRUE;
                    break;
                default:        /*  Wrong option  */
                    usage(0);
                }
            }
        }
        else if ((p = strchr(*argv, '=')) != NULL) 
        {
            char            c;

            c = *p;
            *p = '\0';
            macrotype = M_PERM;     /* doing command-line macros */ 
            setmacro(*argv, p+1);
            macrotype = '\0';
            *p = c;

            argv++;
            argc--;
        }
        else break;
    }

    if ((makefile != NULL) && (strcmp(makefile, "-") == 0))  
        ifile[0] = stdin;
    else if (!makefile)             /*  If no file, then use default */
    {
        if ((ifile[0] = fopen(DEFN1, "r")) == (FILE *)0)
        {
#ifdef NIX
            if ((ifile[0] = fopen(DEFN2, "r")) == (FILE *)0)
            {
#endif
                fatal(MSG[23], DEFN1);  /* unable to open file */
#ifdef NIX
            }
            else strncpy(fname[0],DEFN2,80);
#endif
        }
        else strncpy(fname[0],DEFN1,80);
    }
    else
    {
        if ((ifile[0] = fopen(makefile, "r")) == (FILE *)0)
            fatal(MSG[23], makefile);   /* unable to open file */
        strncpy(fname[0],makefile,80);
    }
    setmacro("MAKEFILE",fname[0]);

    if (egiven) markmacros();       /* make env macros perm */
    if (rules) makerules();         /* use builtin rules */
#ifndef VMS
#ifndef NIX
    setmacro("\\","\\");
#else
    setmacro("\\","/");
#endif
#endif
    setmacro("HOME",getcwd((char *)0,256));

#ifndef OMITBRK
    regs.x.ax = 0x3300;         /* get break state */
    intdos(&regs,&regs);        /* do the dos call */
    svbreak = regs.x.dx;        /* save state */
    regs.x.ax = 0x3301;         /* set break state */
    regs.x.dx = 0x0001;         /* turn it on */
    intdos(&regs,&regs);        /* do the dos call */
    atexit(cleanbrk);
#endif

#ifdef DOLLAR
    setmacro("$", "\244");          /* special high bit set */
#else
    setmacro("$", "$");
#endif

    setmacro(" "," ");
    setmacro("MAKE",myname);
    setmacro("-", LIFNAME);

    input();            /*  Input all the gunga  */
    lineno = 0;         /*  Any calls to error now print no line number */
    ifmessage = '%';    /*  if statement lead-in now '%' */

    np = newname(".SILENT");
    if (np->n_flag & N_TARG) silent = TRUE;

    np = newname(".IGNORE");
    if (np->n_flag & N_TARG) ignore = TRUE;

    np = newname(".SWAP");
    if (np->n_flag & N_TARG) swap = TRUE;

    p = str1;
    *p++ = '-';
    if (egiven) *p++ = 'e';
    if (ignore) *p++ = 'i';
    if (!domake) *p++ = 'n';
    if (quest) *p++ = 'q';
    if (!rules) *p++ = 'r';
    if (silent) *p++ = 's';
    if (dotouch) *p++ = 't';
    if (tabreqd) *p++ = 'u';
    if (swap) *p++ = 'w';
    if (why) *p++ = 'y';
    if (p == (str1+1)) p = str1;
    *p = '\0';
    setmacro("+MAKEFLAGS",str1);

    if (print) prt();       /*  Print out structures  */
    precious();
    snapshot();
    if (!domake) silent = FALSE;
    if (!firstname) fatal(MSG[24]);     /* no targets found */
    circh();                /*  Check circles in target definitions  */

    np = newname(".FIRST");
    if (np->n_flag & N_TARG) make(np,0);

    all = doall;
    if (!argc) estat = make(firstname, 0);
    else while (argc--) estat |= make(newname(*argv++), 0);

    all = 0;
    np = newname(".LAST");
    if (np->n_flag & N_TARG) make(np,0);

    resetcd();
    if (quest) exit((estat)?PXERROR:PXNORMAL);
    else exit(PXNORMAL);
}

void print_header (void)
{

char line1[] = "          ------------------------------------\n";
char line2[] = "          |      2500ad Make Utility         |\n";
char line3[] = "          |           Version 4.2            |\n";
char line4[] = "          |       Copyright (c) 1996         |\n";
char line6[] = "          |                                  |\n";


printf("\n%s%s%s%s%s",line1,line2,line3,line4,line1);
}
